package com.hello.sandbox.app.dispatcher;

import static android.app.Service.START_NOT_STICKY;

import android.app.Service;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.IBinder;
import com.hello.sandbox.SandBoxCore;
import com.hello.sandbox.app.BActivityThread;
import com.hello.sandbox.entity.ServiceRecord;
import com.hello.sandbox.entity.UnbindRecord;
import com.hello.sandbox.proxy.record.ProxyServiceRecord;
import java.util.HashMap;
import java.util.Map;

/** Created by Milk on 4/1/21. * ∧＿∧ (`･ω･∥ 丶　つ０ しーＪ 此处无Bug */
public class AppServiceDispatcher {
  public static final String TAG = "AppServiceDispatcher";

  private static final AppServiceDispatcher sServiceDispatcher = new AppServiceDispatcher();

  private Map<Intent.FilterComparison, ServiceRecord> mService = new HashMap<>();

  public static AppServiceDispatcher get() {
    return sServiceDispatcher;
  }

  private final Handler mHandler = SandBoxCore.get().getHandler();

  public IBinder onBind(Intent proxyIntent) {
    ProxyServiceRecord serviceRecord = ProxyServiceRecord.create(proxyIntent);
    Intent intent = serviceRecord.mServiceIntent;
    ServiceInfo serviceInfo = serviceRecord.mServiceInfo;

    if (intent == null || serviceInfo == null) return null;

    //        Log.d(TAG, "onBind: " + component.toString());

    Service service = getOrCreateService(serviceRecord);
    if (service == null) return null;
    intent.setExtrasClassLoader(service.getClassLoader());

    ServiceRecord record = findRecord(intent);
    record.incrementAndGetBindCount(intent);
    if (record.hasBinder(intent)) {
      if (record.isRebind()) {
        service.onRebind(intent);
        record.setRebind(false);
      }
      return record.getBinder(intent);
    }

    try {
      IBinder iBinder = service.onBind(intent);
      record.addBinder(intent, iBinder);
      return iBinder;
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return null;
  }

  public int onStartCommand(Intent proxyIntent, int flags, int startId) {
    ProxyServiceRecord stubRecord = ProxyServiceRecord.create(proxyIntent);
    if (stubRecord.mServiceIntent == null || stubRecord.mServiceInfo == null) {
      return START_NOT_STICKY;
    }

    //        Log.d(TAG, "onStartCommand: " + component.toString());
    Service service = getOrCreateService(stubRecord);
    if (service == null) return START_NOT_STICKY;
    stubRecord.mServiceIntent.setExtrasClassLoader(service.getClassLoader());
    ServiceRecord record = findRecord(stubRecord.mServiceIntent);
    record.setStartId(stubRecord.mStartId);
    try {
      int i = service.onStartCommand(stubRecord.mServiceIntent, flags, stubRecord.mStartId);
      SandBoxCore.getBActivityManager().onStartCommand(proxyIntent, stubRecord.mUserId);
      return i;
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return START_NOT_STICKY;
  }

  public void onDestroy() {
    if (mService.size() > 0) {
      for (ServiceRecord record : mService.values()) {
        try {
          record.getService().onDestroy();
        } catch (Throwable e) {
          e.printStackTrace();
        }
      }
    }
    mService.clear();
    //        Log.d(TAG, "onDestroy: ");
  }

  public void onConfigurationChanged(Configuration newConfig) {
    if (mService.size() > 0) {
      for (ServiceRecord record : mService.values()) {
        try {
          record.getService().onConfigurationChanged(newConfig);
        } catch (Throwable e) {
          e.printStackTrace();
        }
      }
    }
    //        Log.d(TAG, "onConfigurationChanged");
  }

  public void onLowMemory() {
    if (mService.size() > 0) {
      for (ServiceRecord record : mService.values()) {
        try {
          record.getService().onLowMemory();
        } catch (Throwable e) {
          e.printStackTrace();
        }
      }
    }
    //        Log.d(TAG, "onLowMemory");
  }

  public void onTrimMemory(int level) {
    if (mService.size() > 0) {
      for (ServiceRecord record : mService.values()) {
        try {
          record.getService().onTrimMemory(level);
        } catch (Throwable e) {
          e.printStackTrace();
        }
      }
    }
    // Log.d(TAG, "onTrimMemory");
  }

  public boolean onUnbind(Intent proxyIntent) {
    ProxyServiceRecord stubRecord = ProxyServiceRecord.create(proxyIntent);
    if (stubRecord.mServiceIntent == null || stubRecord.mServiceInfo == null) {
      return false;
    }
    Intent intent = stubRecord.mServiceIntent;

    try {
      UnbindRecord unbindRecord =
          SandBoxCore.getBActivityManager()
              .onServiceUnbind(proxyIntent, BActivityThread.getUserId());
      if (unbindRecord == null) return false;

      Service service = getOrCreateService(stubRecord);
      if (service == null) return false;

      stubRecord.mServiceIntent.setExtrasClassLoader(service.getClassLoader());

      ServiceRecord record = findRecord(intent);

      boolean destroy = unbindRecord.getStartId() == 0;
      if (destroy || record.decreaseConnectionCount(intent)) {
        boolean b = service.onUnbind(intent);
        if (destroy) {
          service.onDestroy();
          SandBoxCore.getBActivityManager()
              .onServiceDestroy(proxyIntent, BActivityThread.getUserId());
          mService.remove(new Intent.FilterComparison(intent));
        }
        record.setRebind(true);
        //                Log.d(TAG, "onUnbind：" +
        // stubRecord.mServiceIntent.getComponent().toString());
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return false;
  }

  public IBinder peekService(Intent intent) {
    ServiceRecord record = findRecord(intent);
    if (record == null) {
      return null;
    }
    return record.getBinder(intent);
  }

  public void stopService(Intent intent) {
    if (intent == null) return;
    ServiceRecord record = findRecord(intent);
    if (record == null) return;
    if (record.getService() != null) {
      boolean destroy = record.getStartId() > 0;
      try {
        if (destroy) {
          mHandler.post(() -> record.getService().onDestroy());
          SandBoxCore.getBActivityManager().onServiceDestroy(intent, BActivityThread.getUserId());
          mService.remove(new Intent.FilterComparison(intent));
        }
      } catch (Throwable e) {
        e.printStackTrace();
      }
    }
  }

  private ServiceRecord findRecord(Intent intent) {
    return mService.get(new Intent.FilterComparison(intent));
  }

  private Service getOrCreateService(ProxyServiceRecord proxyServiceRecord) {
    Intent intent = proxyServiceRecord.mServiceIntent;
    ServiceInfo serviceInfo = proxyServiceRecord.mServiceInfo;
    IBinder token = proxyServiceRecord.mToken;

    ServiceRecord record = findRecord(intent);
    if (record != null && record.getService() != null) {
      return record.getService();
    }
    Service service = BActivityThread.currentActivityThread().createService(serviceInfo, token);
    if (service == null) return null;
    record = new ServiceRecord();
    record.setService(service);
    mService.put(new Intent.FilterComparison(intent), record);
    return service;
  }
}
